package org.blxt.maven;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;

/**
 * Maven插件：统计src/main目录中Java代码的注释率
 */
@Mojo(name = "comment-rate", defaultPhase = LifecyclePhase.VALIDATE)
public class CommentRateMojo extends AbstractMojo {

    @Parameter(defaultValue = "${project}", readonly = true, required = true)
    private MavenProject project;

    @Parameter(property = "sourceDirectory", defaultValue = "${project.basedir}/src/main/java")
    private File sourceDirectory;

    /** 文件的最小注释率 */
    @Parameter(property = "minCommentRate", defaultValue = "0.0")
    private double minCommentRate;

    /** 函数方法的最小注释率 */
    @Parameter(property = "minCommentMethodRate", defaultValue = "0.0")
    private double minCommentMethodRate;

    @Parameter(property = "failOnLowRate", defaultValue = "false")
    private boolean failOnLowRate;

    @Parameter(property = "showFileDetails", defaultValue = "false")
    private boolean showFileDetails;

    // 仅打印不达标的文件
    @Parameter(property = "showFileDetailsOnlyFail", defaultValue = "false")
    private boolean showFileDetailsOnlyFail;

    @Parameter(property = "includeImportLines", defaultValue = "false")
    private boolean includeImportLines;

    @Parameter(property = "includeBlankLines", defaultValue = "false")
    private boolean includeBlankLines;

    /** 函数方法统计 */
    @Parameter(property = "methodStats", defaultValue = "false")
    private boolean methodStats;

    List<String> logCache = new ArrayList<>();

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        if (!sourceDirectory.exists()) {
            logInfo("源代码目录不存在: " + sourceDirectory.getAbsolutePath());
            return;
        }

        if (!sourceDirectory.isDirectory()) {
            throw new MojoExecutionException("指定的源代码路径不是目录: " + sourceDirectory.getAbsolutePath());
        }
        logCache.add("# Java代码注释率统计分析\r\n");
        logInfo("------------------------");
        logInfo("* 开始分析Java代码注释率...");
        logInfo("* 是否计算空行:" + includeBlankLines);
        logInfo("* 是否计算Import行:" + includeImportLines);
        logInfo("* 源代码目录: " + sourceDirectory.getAbsolutePath());
        logInfo("------------------------");

        if(minCommentMethodRate == 0.0 && minCommentRate != 0.0) {
            minCommentMethodRate = minCommentRate;
        }
        try {
            logCache.add("## 文件记录\n");
            CommentRateStatistics statistics = analyzeSourceDirectory();
            statistics.setMinCommentRate(minCommentRate);
            statistics.setMinCommentMethodRate(minCommentMethodRate);
            // 输出统计结果
            logInfo("\n" + statistics.toString());

            // 检查是否满足最小注释率要求
            if (failOnLowRate && statistics.getCommentRate() < minCommentRate) {
                throw new MojoFailureException(
                    String.format("代码注释率 %.2f%% 低于要求的最小值 %.2f%%", 
                        statistics.getCommentRate(), minCommentRate)
                );
            }
            if (statistics.getCommentRate() < minCommentRate) {
                logWarn(String.format("代码注释率 %.2f%% 低于推荐值 %.2f%%",
                        statistics.getCommentRate(), minCommentRate));
            }
            // 保存日志
            saveLogCacheToFile();
        } catch (IOException e) {
            throw new MojoExecutionException("分析源代码文件时发生错误", e);
        }
    }

    private CommentRateStatistics analyzeSourceDirectory() throws IOException {
        CommentRateStatistics statistics = new CommentRateStatistics();
        JavaFileAnalyzer analyzer = new JavaFileAnalyzer();
        JavaMethodAnalyzer analyzerMethod = new JavaMethodAnalyzer();

        Path sourcePath = Paths.get(sourceDirectory.getAbsolutePath());

        AtomicInteger countOk = new AtomicInteger();
        AtomicInteger countFalse = new AtomicInteger();
        AtomicInteger countMethod = new AtomicInteger();
        AtomicInteger countMethodFalse = new AtomicInteger();

        try (Stream<Path> paths = Files.walk(sourcePath)) {
            paths.filter(Files::isRegularFile)
                 .filter(path -> path.toString().endsWith(".java"))
                 .forEach(path -> {
                     try {
                         File file = path.toFile();
                         JavaFileAnalyzer.FileStats fileStats = analyzer.analyzeFile(file, includeImportLines, includeBlankLines);
                         
                         statistics.addFile(
                             fileStats.getTotalLines(),
                             fileStats.getCommentLines(),
                             fileStats.getCodeLines(),
                             fileStats.getBlankLines()
                         );

                         // 文件解析
                         double fileCommentRate = fileStats.getCodeLines() > 0 ?
                             (double) fileStats.getCommentLines() / fileStats.getCodeLines() * 100 : 0.0;

                         boolean falFail = false;
                         if(fileCommentRate > minCommentRate){
                             // 达标
                             countOk.getAndIncrement();
                             falFail = true;
                         }
                         else{
                             // 不达标
                             countFalse.getAndIncrement();
                             falFail = false;
                         }
                         // 打印信息
                         boolean falShow = false;
                         if(showFileDetails && (!showFileDetailsOnlyFail || !falFail)){
                             falShow = true;
                             putFileLog(sourcePath, path, fileStats, fileCommentRate);
                         }

                         // 函数方法解析 - 只显示达标文件函数方法注释统计
                         if(methodStats){ //  && fileCommentRate > minCommentRate
                             // 方法函数注释解析
                             boolean falMethod = true;
                             List<JavaMethodAnalyzer.MethodStats> stats = analyzerMethod.analyzeMethods(file, false, false);
                             for (JavaMethodAnalyzer.MethodStats stat : stats) {
                                 double fileCommentRate2 = stat.getCodeLines() > 0 ?
                                         (double) stat.getCommentLines() / stat.getCodeLines() * 100 : 0.0;
                                 if(stat.getMethodCommentLines() > 0){
                                     countMethod.getAndIncrement();
                                 }
                                 else if(fileCommentRate2 < minCommentRate){
                                     if(falMethod){
                                         falMethod = false;
                                         if(!falShow){
                                             putFileLog(sourcePath, path, fileStats, fileCommentRate);
                                         }
                                     }
                                     putFethodLog(stat, fileCommentRate2);
                                     countMethodFalse.getAndIncrement();
                                 }
                             }
                         }
                     } catch (IOException e) {
                         logError("分析文件失败: " + path.toString() + "\r\b" + e.getMessage());
                     }
                 });
        }

        statistics.setCountOk(countOk.get());
        statistics.setCountFalse(countFalse.get());
        statistics.setCountMethod(countMethod.get());
        statistics.setCountMethodFalse(countMethodFalse.get());
        
        return statistics;
    }

    public void putFileLog(Path sourcePath, Path path, JavaFileAnalyzer.FileStats fileStats,  double fileCommentRate){
        String msg = String.format(
                "* 文件: `%s` - 总行数: %d, 注释: %d, 代码: %d, 空白: %d, 注释率: %.2f%%",
                getRelativePath(sourcePath, path).replace("\\", "/"),
                fileStats.getTotalLines(),
                fileStats.getCommentLines(),
                fileStats.getCodeLines(),
                fileStats.getBlankLines(),
                fileCommentRate
        );
        logCache.add(msg);
        if(fileCommentRate > minCommentRate){
            getLog().info(msg);
        }
        else {
            getLog().warn(msg);
        }
    }

    public void putFethodLog(JavaMethodAnalyzer.MethodStats stat, double fileCommentRate){
        String msg = String.format(
                "\t* 方法: `%s()`,方法注释行:%d - 总行数: %d, 注释: %d, 代码: %d, 空白: %d, 注释率: %.2f%%",
                stat.getMethodName(),
                stat.getMethodCommentLines(),
                stat.getTotalLines(),
                stat.getCommentLines(),
                stat.getCodeLines(),
                stat.getBlankLines(),
                fileCommentRate
        );
        if(fileCommentRate > minCommentMethodRate){
            logInfo(msg);
        }
        else {
            logWarn(msg);
        }
    }

    public void logInfo(String msg){
        logCache.add(msg);
        getLog().info(msg);
    }

    public void logWarn(String msg){
        logCache.add(msg);
        getLog().warn(msg);
    }

    public void logError(String msg){
        logCache.add(msg);
        getLog().error(msg);
    }

    /**
     * 将logCache保存到当前项目/log/commentRate.txt文件
     *
     * @throws IOException 如果保存文件时出现错误
     */
    private void saveLogCacheToFile() throws IOException {
        // 获取项目根目录
        Path projectBaseDir = project.getBasedir().toPath();

        // 构建目标文件路径: 项目根目录/log/commentRate.txt
        Path logDir = projectBaseDir.resolve("doc");
        Path logFile = logDir.resolve("注释率统计.md");

        // 如果log目录不存在，则创建
        if (!Files.exists(logDir)) {
            Files.createDirectories(logDir);
        }

        // 写入日志内容到文件
        StringBuilder content = new StringBuilder();
        for (String logEntry : logCache) {
            content.append(logEntry).append(System.lineSeparator());
        }

        String auth =
                """
                * 统计时间：%s
                * 插件：[Java代码注释率统计分析](https://gitee.com/zhangjialei2/comment-rate-maven-plugin.git)
                """
                .formatted(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));

        content.append("\r\n").append(auth);

        Files.writeString(logFile, content.toString());

        getLog().info("日志已保存到: " + logFile.toAbsolutePath().toString());
    }


    private String getRelativePath(Path basePath, Path filePath) {
        try {
            return basePath.relativize(filePath).toString();
        } catch (Exception e) {
            return filePath.toString();
        }
    }
}